package com.sc.nft.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.Maps;
import com.sc.nft.dao.*;
import com.sc.nft.dto.TradePoolConsumerDTO;
import com.sc.nft.entity.*;
import com.sc.nft.entity.dto.BusinessDTO;
import com.sc.nft.entity.dto.CreateTradingPoolOrderDTO;
import com.sc.nft.entity.dto.TradingPoolOrderDTO;
import com.sc.nft.entity.dto.TradingPoolTransactionRecordsDTO;
import com.sc.nft.entity.vo.CreateTradingPoolOrderVO;
import com.sc.nft.entity.vo.DingLianDealPracticalVO;
import com.sc.nft.entity.vo.GetDaoByIdVO;
import com.sc.nft.entity.vo.TradingPoolTransactionRecordsVO;
import com.sc.nft.entity.vo.tradingPool.TradingPoolOrderDetailVO;
import com.sc.nft.entity.vo.tradingPool.TradingPoolOrderPageVO;
import com.sc.nft.entity.vo.tradingPool.TradingPoolOrderVO;
import com.sc.nft.enums.*;
import com.sc.nft.exception.GlobalRunTimeException;
import com.sc.nft.helper.MessageQueueHelper;
import com.sc.nft.service.TradingPoolOrderService;
import com.sc.nft.service.UserCollectionService;
import com.sc.nft.service.UserConsensusService;
import com.sc.nft.service.UserWalletService;
import com.sc.nft.sup.ErrorCode;
import com.sc.nft.util.OrderNoUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.compress.utils.Lists;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronizationAdapter;
import org.springframework.transaction.support.TransactionSynchronizationManager;

import java.math.BigDecimal;
import java.time.LocalTime;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author PC
 */
@Slf4j
@RequiredArgsConstructor
@Service
public class TradingPoolOrderServiceImpl implements TradingPoolOrderService {
    private final TradingPoolOrderDao tradingPoolOrderDao;
    private final DigitalCollectionDao digitalCollectionDao;
    private final UserInfoDao userInfoDao;
    private final UserCreatorInfoDao userCreatorInfoDao;
    private final ShowLevelDao showLevelDao;
    private final TradingPoolConfigDao tradingPoolConfigDao;
    private final ExitConsensusUserDao exitConsensusUserDao;
    private final TradingPoolVirtualAccountDao tradingPoolVirtualAccountDao;
    private final UserWalletService userWalletService;
    private final UserCollectionService userCollectionService;
    private final UserConsensusService userConsensusService;
    private final PayOrderDao payOrderDao;
    private final MessageQueueHelper messageQueueHelper;
    private final FilterIncomeListDao filterIncomeListDao;


    @Override
    public CreateTradingPoolOrderVO createOrder(CreateTradingPoolOrderDTO createTradingPoolOrderDTO) {
        return null;
    }


    @Override
    public IPage<TradingPoolOrderVO> pageTradeInOrder(Integer pageNo, Integer pageSize, Date searchDate, Long userId) {
        DateTime startTime = null;
        DateTime endTime = null;
        if (Objects.nonNull(searchDate)) {
            startTime = DateUtil.beginOfMonth(searchDate);
            endTime = DateUtil.endOfMonth(searchDate);
        }
        Page<TradingPoolOrder> tradingPoolOrderPage = tradingPoolOrderDao.pageTradeInOrder(new Page<>(pageNo, pageSize), startTime, endTime, userId, TradingPoolTransactionTypeEnum.BUY.getCode());
        return getTradingPoolOrderVOIPage(tradingPoolOrderPage);
    }

    @Override
    public IPage<TradingPoolOrderVO> pageTradeOutOrder(Integer pageNo, Integer pageSize, Date searchDate, Long userId) {
        DateTime startTime = null;
        DateTime endTime = null;
        if (Objects.nonNull(searchDate)) {
            startTime = DateUtil.beginOfMonth(searchDate);
            endTime = DateUtil.endOfMonth(searchDate);
        }
        Page<TradingPoolOrder> tradingPoolOrderPage = tradingPoolOrderDao.pageTradeInOrder(new Page<>(pageNo, pageSize), startTime, endTime, userId, TradingPoolTransactionTypeEnum.SELL.getCode());
        return getTradingPoolOrderVOIPage(tradingPoolOrderPage);
    }

    @Override
    public TradingPoolOrderDetailVO tradeOrderDetail(Long id) {
        TradingPoolOrder order = tradingPoolOrderDao.getById(id);
        Assert.notNull(order, () -> new GlobalRunTimeException("订单不存在"));

        DigitalCollection collection = digitalCollectionDao.getById(order.getCollectionId());
        Assert.notNull(collection, () -> new GlobalRunTimeException("藏品不存在"));

        ShowLevel showLevel = showLevelDao.getById(collection.getShowLevel());

        TradingPoolOrderDetailVO orderDetail = new TradingPoolOrderDetailVO();
        orderDetail.setFirstTitle(collection.getFirstTitle());
        orderDetail.setCreationContent(collection.getCreationContent());
        orderDetail.setNumbering(order.getNumbering());
        orderDetail.setAmount(order.getPrice());
        orderDetail.setOrderNumber(order.getOrderNo());
        orderDetail.setGas(order.getGas());
        orderDetail.setDealTime(order.getDealTime());
        orderDetail.setRealAmount(order.getRealAmount().toPlainString());
        orderDetail.setCharge(order.getCharge().toPlainString());
        orderDetail.setOrderStatus(TradingStatusEnum.SUCCESS);
        orderDetail.setLevel(Objects.nonNull(showLevel) ? showLevel.getShowLevelName() : null);
        UserInfo userInfo = userInfoDao.getById(order.getUserId());
        GetDaoByIdVO dao = userCreatorInfoDao.getDaoById(order.getDaoId());
        if (order.getTransactionType() == TradingPoolTransactionTypeEnum.BUY) {
            orderDetail.setBuyUserName(userInfo.getNickName());
            orderDetail.setSellerUserName(dao.getDaoName());
        }
        if (order.getTransactionType() == TradingPoolTransactionTypeEnum.SELL) {
            orderDetail.setSellerUserName(userInfo.getNickName());
            orderDetail.setBuyUserName(dao.getDaoName());
        }
        return orderDetail;
    }

    @Override
    public IPage<TradingPoolTransactionRecordsVO> pageTransactionRecords(TradingPoolTransactionRecordsDTO ttr) {
        Page<TradingPoolOrder> tradingPoolOrderPage = tradingPoolOrderDao.pageTransactionRecords(ttr.getId(), new Page<>(ttr.getPageNo(), ttr.getPageSize()), ttr.getDaoId());
        Map<Long, DigitalCollection> map = getDigitalCollectionMap(tradingPoolOrderPage.getRecords());
        return tradingPoolOrderPage.convert(x -> {
            TradingPoolTransactionRecordsVO transactionRecordsVO = new TradingPoolTransactionRecordsVO();
            DigitalCollection digitalCollection = map.get(x.getCollectionId());
            if (Objects.nonNull(digitalCollection)) {
                transactionRecordsVO.setFirstTitle(digitalCollection.getFirstTitle());
            }
            transactionRecordsVO.setNumbering(x.getNumbering());
            transactionRecordsVO.setDealTime(x.getDealTime());
            transactionRecordsVO.setAmount(x.getPrice());
            transactionRecordsVO.setSumCount(digitalCollection.getSumCount());
            return transactionRecordsVO;
        });
    }

    @Override
    public IPage<TradingPoolOrderPageVO> orderPage(TradingPoolOrderDTO request) {
        Long userId = null;
        if (StrUtil.isNotBlank(request.getUserTel())) {
            UserInfo userInfo = userInfoDao.getByUserTel(request.getUserTel());
            if (Objects.nonNull(userInfo)) {
                userId = userInfo.getId();
            }
        }
        IPage<TradingPoolOrder> tradingPoolOrderIPage = tradingPoolOrderDao.orderPage(request.toMp(), request.getDaoId(), request.getOrderNo(), request.getTransactionType(), userId, request.getStartTime(), request.getEndTime());
        //region 品牌
        List<Long> daoIdList = CollectionUtil.isNotEmpty(tradingPoolOrderIPage.getRecords()) ? tradingPoolOrderIPage.getRecords().stream().map(TradingPoolOrder::getDaoId).distinct().collect(Collectors.toList()) : Lists.newArrayList();
        List<UserCreatorInfo> userCreatorInfoList = CollectionUtil.isNotEmpty(daoIdList) ? userCreatorInfoDao.listByIds(daoIdList) : Lists.newArrayList();
        Map<Long, UserCreatorInfo> userCreatorInfoMap = CollectionUtil.isNotEmpty(userCreatorInfoList) ? userCreatorInfoList.stream().collect(Collectors.toMap(UserCreatorInfo::getId, Function.identity())) : Maps.newHashMap();
        //endregion
        //region 藏品
        List<Long> collectionIdList = CollectionUtil.isNotEmpty(tradingPoolOrderIPage.getRecords()) ? tradingPoolOrderIPage.getRecords().stream().map(TradingPoolOrder::getCollectionId).distinct().collect(Collectors.toList()) : Lists.newArrayList();
        List<DigitalCollection> collectionList = CollectionUtil.isNotEmpty(collectionIdList) ? digitalCollectionDao.getByIds(collectionIdList) : Lists.newArrayList();
        Map<Long, DigitalCollection> collectionMap = CollectionUtil.isNotEmpty(collectionList) ? collectionList.stream().collect(Collectors.toMap(DigitalCollection::getId, Function.identity())) : Maps.newHashMap();
        //endregion
        //region 用户
        List<Long> userIdList = CollectionUtil.isNotEmpty(tradingPoolOrderIPage.getRecords()) ? tradingPoolOrderIPage.getRecords().stream().map(TradingPoolOrder::getUserId).distinct().collect(Collectors.toList()) : Lists.newArrayList();
        List<UserInfo> userInfoList = CollectionUtil.isNotEmpty(userIdList) ? userInfoDao.getByIdList(userIdList) : Lists.newArrayList();
        Map<Long, UserInfo> userInfoMap = CollectionUtil.isNotEmpty(userInfoList) ? userInfoList.stream().collect(Collectors.toMap(UserInfo::getId, Function.identity())) : Maps.newHashMap();
        //endregion
        return tradingPoolOrderIPage.convert(v -> {
            TradingPoolOrderPageVO tradingPoolOrderPageVO = new TradingPoolOrderPageVO();
            //region 品牌
            tradingPoolOrderPageVO.setDaoId(v.getDaoId());
            UserCreatorInfo userCreatorInfo = userCreatorInfoMap.get(v.getDaoId());
            tradingPoolOrderPageVO.setDaoName(Objects.nonNull(userCreatorInfo) ? userCreatorInfo.getDaoName() : "");
            //endregion
            //region 藏品
            tradingPoolOrderPageVO.setCollectionId(v.getCollectionId());
            DigitalCollection collection = collectionMap.get(v.getCollectionId());
            tradingPoolOrderPageVO.setCollectionName(Objects.nonNull(collection) ? collection.getFirstTitle() : "");
            //endregion
            tradingPoolOrderPageVO.setTransactionType(v.getTransactionType());
            tradingPoolOrderPageVO.setTransactionTypeName(v.getTransactionType().getDescription());
            //region 用户
            tradingPoolOrderPageVO.setUserId(v.getUserId());
            UserInfo userInfo = userInfoMap.get(v.getUserId());
            tradingPoolOrderPageVO.setUserTel(Objects.nonNull(userInfo) ? userInfo.getUserTel() : "");
            tradingPoolOrderPageVO.setNickName(Objects.nonNull(userInfo) ? userInfo.getNickName() : "");
            //endregion
            tradingPoolOrderPageVO.setPrice(v.getPrice());
            tradingPoolOrderPageVO.setCharge(v.getCharge());
            tradingPoolOrderPageVO.setGas(v.getGas());
            tradingPoolOrderPageVO.setPayTime(v.getPayTime());
            tradingPoolOrderPageVO.setOrderNo(v.getOrderNo());
            return tradingPoolOrderPageVO;
        });
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void business(BusinessDTO businessDTO, UserInfo userInfo, String ip) {
        Assert.isTrue(StrUtil.equals(userInfo.getTransactionPassword(), businessDTO.getDealPwd()), () -> new GlobalRunTimeException("支付密码不正确"));
        // 先进行前置校验.校验内容 用户在共识区 交易池状态 库存校验 价格校验
        ExitConsensusUser exitConsensusUser = exitConsensusUserDao.getByUserId(userInfo.getId());
        if (BeanUtil.isNotEmpty(exitConsensusUser) && exitConsensusUser.getStatus()) {
            throw new GlobalRunTimeException("您不在生态共识区中");
        }

        TradingPoolConfig tradingPoolConfig = tradingPoolConfigDao.getById(businessDTO.getId());
        if (BeanUtil.isEmpty(tradingPoolConfig)) {
            throw new GlobalRunTimeException("交易池不存在");
        }
        if (!tradingPoolConfig.getStatus()) {
            throw new GlobalRunTimeException("交易池已关闭");
        }
        if (!NumberUtil.equals(tradingPoolConfig.getCurrentPrice(), businessDTO.getPresentPrice())) {
            throw new GlobalRunTimeException("购买失败,价格发生改变");
        }
        // 当前时间必须在交易池时间范围内 否则的话不能进行交易
        // 根据时间推算,当前时间如果小于开始时间则是未开始 如果大于结束时间则是已结束,如果status=0的话则是关闭 如果在时间范围内则是正常
        // 转换后的当前时间
        DateTime localityTime = DateTime.of(LocalTime.now().truncatedTo(ChronoUnit.SECONDS).toString(), "HH:mm:ss");
        // 转换配置开始时间
        DateTime startTime = DateTime.of(tradingPoolConfig.getStartTime());
        // 转换配置结束时间
        DateTime endTime = DateTime.of(tradingPoolConfig.getEndTime());
        // 判断localityTime在不在startTime和endTime之间,在的话则是正常
        if (!(localityTime.isAfter(startTime) && localityTime.isBefore(endTime))) {
            throw new GlobalRunTimeException("交易池不在交易时间范围内");
        }

        // 库存校验
        if (tradingPoolConfig.getCurrentStock() <= 0) {
            throw new GlobalRunTimeException("库存不足");
        }

        if (NumberUtil.equals(tradingPoolConfig.getCurrentAmount(), BigDecimal.ZERO)) {
            throw new GlobalRunTimeException("当前交易池资金禁止买卖");
        }

        if (NumberUtil.equals(tradingPoolConfig.getCurrentPrice(), BigDecimal.ZERO)) {
            throw new GlobalRunTimeException("当前交易池资金禁止买卖");
        }

        // 前置判断完成,开始根据订单的买or卖,进行不同的操作
        switch (businessDTO.getType()) {
            case BUY:
                // 买入
                buy(tradingPoolConfig, businessDTO, userInfo, ip);
                break;
            case SELL:
                // 卖出
                sell(tradingPoolConfig, businessDTO, userInfo, ip);
                break;
            default:
                throw new GlobalRunTimeException("交易类型错误");
        }
    }

    @Override
    public List<DingLianDealPracticalVO> dealTradingPracticalInConsensus() {
        return tradingPoolOrderDao.dealTradingPracticalInConsensus();
    }

    @Override
    public List<DingLianDealPracticalVO> dealTradingPracticalNotInConsensus() {
        return tradingPoolOrderDao.dealTradingPracticalNotInConsensus();
    }

    private void sell(TradingPoolConfig tradingPoolConfig, BusinessDTO businessDTO, UserInfo userInfo, String ip) {
        // 如果是卖的话,就是用户-交易池 用户+钱 扣除藏品 交易池扣库存
        Long daoId = tradingPoolConfig.getDaoId();
        // 通过daoId获取绑定的虚拟账户
        TradingPoolVirtualAccount tradingPoolVirtualAccount = tradingPoolVirtualAccountDao.getByDaoId(daoId);
        if (BeanUtil.isEmpty(tradingPoolVirtualAccount)) {
            throw new GlobalRunTimeException("该dao无法完成交易,请联系客服人员解答");
        }
        // 看交易池里面的钱够不够
        if (NumberUtil.isGreater(tradingPoolConfig.getCurrentPrice(), tradingPoolConfig.getCurrentAmount())) {
            throw new GlobalRunTimeException("交易池资金不足");
        }
        // 验证用户藏品是否充足
        UserCollection userCollection = userCollectionService.getUserByCollectionIdOne(userInfo.getId(), tradingPoolConfig.getCollectionId());
        if (BeanUtil.isEmpty(userCollection)) {
            throw new GlobalRunTimeException("当前不存在可以出售的藏品");
        }

        //判断是否是市值账号
        FilterIncomeList filterIncomeList = filterIncomeListDao.getIsTeamIncomeByUserIdAndDaoId(tradingPoolConfig.getDaoId(), userInfo.getId());
        boolean isFilter = ObjectUtil.isNotNull(filterIncomeList);

        BigDecimal payAmount = tradingPoolConfig.getCurrentPrice();

        // 生成订单
        TradingPoolOrder tradingPoolOrder = new TradingPoolOrder();
        tradingPoolOrder.setTradingPoolConfigId(tradingPoolConfig.getId());
        tradingPoolOrder.setDaoId(tradingPoolConfig.getDaoId());
        tradingPoolOrder.setCollectionId(tradingPoolConfig.getCollectionId());
        tradingPoolOrder.setUserCollectionId(userCollection.getId());
        tradingPoolOrder.setUserId(userInfo.getId());
        tradingPoolOrder.setGas(isFilter ? BigDecimal.ZERO : tradingPoolConfig.getGas());
        tradingPoolOrder.setStatus(OrderStatusEnum.PAYMENT);
        tradingPoolOrder.setOrderNo(OrderNoUtil.getOrderNo());
        tradingPoolOrder.setDealTime(DateUtil.date());
        tradingPoolOrder.setPayTime(DateUtil.date());
        tradingPoolOrder.setCloseTime(DateUtil.date());
        tradingPoolOrder.setRealCloseTime(DateUtil.date());
        tradingPoolOrder.setMaxIncreasePercentage(businessDTO.getMaxIncreasePercentage());
        tradingPoolOrder.setMaxDecreasePercentage(businessDTO.getMaxDecreasePercentage());
        tradingPoolOrder.setShareProfitPanelId(tradingPoolConfig.getShareProfitPanelId());
        tradingPoolOrder.setNumbering(userCollection.getNumbering());
        tradingPoolOrder.setTransactionType(TradingPoolTransactionTypeEnum.SELL);
        tradingPoolOrder.setSettlementFlag(Boolean.FALSE);

        // 当前价格
        BigDecimal currentPrice = tradingPoolConfig.getCurrentPrice();
        // 最低价
        BigDecimal maxDecreasePrice = (BigDecimal.ONE.subtract(businessDTO.getMaxDecreasePercentage())).multiply(businessDTO.getPresentPrice());
        // 最高价
        BigDecimal maxIncreasePrice = (businessDTO.getMaxIncreasePercentage().add(BigDecimal.ONE)).multiply(businessDTO.getPresentPrice());
        if (NumberUtil.isLess(payAmount, maxDecreasePrice) || NumberUtil.isGreater(payAmount, maxIncreasePrice)) {
            throw new GlobalRunTimeException("当前价格不在市场价格区间");
        }
        // 判断当前价格是否在规定的范围内
        if (NumberUtil.isLess(maxDecreasePrice, tradingPoolConfig.getMinPrice()) || NumberUtil.isGreater(maxIncreasePrice, tradingPoolConfig.getMaxPrice())) {
            throw new GlobalRunTimeException("当前价格不在交易范围内");
        }


        // 手续费
        BigDecimal charge = isFilter ? BigDecimal.ZERO : currentPrice.multiply(tradingPoolConfig.getFeeRate()).setScale(2, BigDecimal.ROUND_DOWN);
        tradingPoolOrder.setPresentPrice(businessDTO.getPresentPrice());
        tradingPoolOrder.setRealAmount(currentPrice.subtract(charge));
        tradingPoolOrder.setPrice(currentPrice);
        tradingPoolOrder.setCharge(charge);
        tradingPoolOrder.setNumbering(userCollection.getNumbering());


        // 生成支付订单
        PayOrder payOrder = new PayOrder();
        payOrder.setUserId(tradingPoolVirtualAccount.getUserId());
        payOrder.setPayType(PayMethodEnum.WALLET_PAY);
        payOrder.setReqTime(DateUtil.date());
        payOrder.setCloseTime(DateUtil.date());
        payOrder.setOrderNo(tradingPoolOrder.getOrderNo());
        payOrder.setReqIp(ip);
        payOrder.setOrderType(PayTypeEnum.TRADING_POOL);
        payOrder.setPayAmount(currentPrice);
        payOrder.setChannel(PayChannelEnum.NATIVE);
        payOrder.setStatus(PayStatusEnum.SUCCESS);
        payOrder.setPayTime(DateUtil.date());
        payOrder.setPayBalanceAmount(BigDecimal.ZERO);
        payOrderDao.save(payOrder);
        tradingPoolOrderDao.save(tradingPoolOrder);

        // nft回收
        userCollectionService.recoverTradingPoolCollection(userCollection);

        // 查询出来藏品信息
        DigitalCollection digitalCollection = digitalCollectionDao.getById(tradingPoolConfig.getCollectionId());
        //更新当前价格.更新当前库存,更新当前资金池
        Boolean inventoryDeduction = tradingPoolConfigDao.sellInventoryDeduction(tradingPoolConfig.getId(), tradingPoolOrder.getPrice(), digitalCollection.getSumCount());
        if (!inventoryDeduction) {
            throw new GlobalRunTimeException("当前库存异常");
        }
        // 扣除共识度
        userConsensusService.adjustConsensusService(userInfo.getId(), digitalCollection.getId(), Convert.toBigDecimal(-1), UserConsensusTypeEnums.TRADING_POOL_SELL, digitalCollection.getFirstTitle(), null);

        TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
            @Override
            public void afterCommit() {
                //卖出发送分润消息
                TradePoolConsumerDTO tradePoolConsumerDTO = new TradePoolConsumerDTO();
                tradePoolConsumerDTO.setTradeOrderId(tradingPoolOrder.getId());
                messageQueueHelper.sendMessage(MessageTopic.TRADE_POOL_INCOME, tradePoolConsumerDTO, TradingPoolMessageEnum.TRADE_SELLE_ORDER.name(), TradingPoolMessageEnum.TRADE_SELLE_ORDER.getDesc() + tradePoolConsumerDTO.getTradeOrderId());

                // 发送mq来同步今日成交量 最高价和最低价
                messageQueueHelper.sendMessage(MessageTopic.QUANTITY_AND_PRICE_SYNCHRONIZATION, tradingPoolConfig.getId(), TradingPoolMessageEnum.QUANTITY_AND_PRICE_SYNCHRONIZATION.name(), TradingPoolMessageEnum.QUANTITY_AND_PRICE_SYNCHRONIZATION.getDesc() + tradingPoolOrder.getId());
            }
        });

    }

    private void buy(TradingPoolConfig tradingPoolConfig, BusinessDTO businessDTO, UserInfo userInfo, String ip) {
        // 如果是买的话,那么就是交易池-用户手里   用户扣钱.获得藏品.交易池扣库存
        Long daoId = tradingPoolConfig.getDaoId();
        // 通过daoId获取绑定的虚拟账户
        TradingPoolVirtualAccount tradingPoolVirtualAccount = tradingPoolVirtualAccountDao.getByDaoId(daoId);
        if (BeanUtil.isEmpty(tradingPoolVirtualAccount)) {
            throw new GlobalRunTimeException("该dao无法完成交易,请联系客服人员解答");
        }

        UserWallet userWallet = userWalletService.getByUserId(userInfo.getId());
        // 扣钱 不先把钱扣了,数据填充不进去
        BigDecimal payBalance = userWallet.getPayBalance();
        BigDecimal balance = userWallet.getBalance();
        BigDecimal amount = payBalance.add(balance);
        BigDecimal payAmount = tradingPoolConfig.getCurrentPrice();
        // 判断用户的余额是否足够
        if (NumberUtil.isLess(amount, payAmount)) {
            throw new GlobalRunTimeException("余额不足");
        }

        // 给用户发NFT,nft也要先发掉,否则数据填充不进去.


        // 通过用户的id和dao的虚拟号id进行绑定.生成假账单走对账
        // 生成订单
        TradingPoolOrder tradingPoolOrder = new TradingPoolOrder();
        tradingPoolOrder.setTradingPoolConfigId(tradingPoolConfig.getId());
        tradingPoolOrder.setDaoId(tradingPoolConfig.getDaoId());
        tradingPoolOrder.setCollectionId(tradingPoolConfig.getCollectionId());
        GetDaoByIdVO getDaoByIdVO = userCreatorInfoDao.getDaoById(tradingPoolConfig.getDaoId());
        tradingPoolOrder.setUserCollectionId(getDaoByIdVO.getId());
        tradingPoolOrder.setUserId(userInfo.getId());
        tradingPoolOrder.setGas(BigDecimal.ZERO);
        tradingPoolOrder.setStatus(OrderStatusEnum.PAYMENT);
        tradingPoolOrder.setOrderNo(OrderNoUtil.getOrderNo());
        tradingPoolOrder.setDealTime(DateUtil.date());
        tradingPoolOrder.setPayTime(DateUtil.date());
        tradingPoolOrder.setCloseTime(DateUtil.date());
        tradingPoolOrder.setRealCloseTime(DateUtil.date());
        tradingPoolOrder.setMaxIncreasePercentage(businessDTO.getMaxIncreasePercentage());
        tradingPoolOrder.setMaxDecreasePercentage(businessDTO.getMaxDecreasePercentage());
        tradingPoolOrder.setShareProfitPanelId(tradingPoolConfig.getShareProfitPanelId());
        tradingPoolOrder.setTransactionType(TradingPoolTransactionTypeEnum.BUY);
        tradingPoolOrder.setSettlementFlag(Boolean.TRUE);


        // 最低价
        BigDecimal maxDecreasePrice = (BigDecimal.ONE.subtract(businessDTO.getMaxDecreasePercentage())).multiply(businessDTO.getPresentPrice());
        // 最高价
        BigDecimal maxIncreasePrice = (businessDTO.getMaxIncreasePercentage().add(BigDecimal.ONE)).multiply(businessDTO.getPresentPrice());
        if (NumberUtil.isLess(payAmount, maxDecreasePrice) || NumberUtil.isGreater(payAmount, maxIncreasePrice)) {
            throw new GlobalRunTimeException("当前价格不在市场价格区间");
        }
        // 判断当前价格是否在规定的范围内
        if (NumberUtil.isLess(payAmount, tradingPoolConfig.getMinPrice()) || NumberUtil.isGreater(payAmount, tradingPoolConfig.getMaxPrice())) {
            throw new GlobalRunTimeException("当前价格不在交易范围内");
        }

        tradingPoolOrder.setPresentPrice(businessDTO.getPresentPrice());
        tradingPoolOrder.setRealAmount(payAmount);
        tradingPoolOrder.setPrice(payAmount);
        tradingPoolOrder.setCharge(BigDecimal.ZERO);


        // 生成支付订单
        PayOrder payOrder = new PayOrder();
        payOrder.setUserId(userInfo.getId());
        payOrder.setPayType(PayMethodEnum.WALLET_PAY);
        payOrder.setReqTime(DateUtil.date());
        payOrder.setCloseTime(DateUtil.date());
        payOrder.setOrderNo(tradingPoolOrder.getOrderNo());
        payOrder.setReqIp(ip);
        payOrder.setOrderType(PayTypeEnum.TRADING_POOL);
        payOrder.setPayAmount(payAmount);
//        payOrder.setBalanceAmount();
//        payOrder.setPayBalanceAmount();
        payOrder.setChannel(PayChannelEnum.NATIVE);
        payOrder.setStatus(PayStatusEnum.SUCCESS);
        payOrder.setPayTime(DateUtil.date());


        if (payBalance.compareTo(payAmount) >= 0) { //如果支付账户足够 直接从支付账户扣
            payOrder.setPayBalanceAmount(payAmount);
            Assert.isTrue(userWalletService.subBalance(userWallet, payAmount, tradingPoolOrder.getId(), WalletDetailTypeEnum.TRANSACTION_POOL_PAYMENT, UserBalanceTypeEnum.PAY_BALANCE), () -> new GlobalRunTimeException(ErrorCode.PAYMENT_FAILED));
        } else {//如果支付账户没钱
            if (payBalance.compareTo(BigDecimal.ZERO) == 0) {//支付账户没钱,直接扣提现账户
                payOrder.setBalanceAmount(payAmount);
                Assert.isTrue(userWalletService.subBalance(userWallet, payAmount, tradingPoolOrder.getId(), WalletDetailTypeEnum.TRANSACTION_POOL_PAYMENT, UserBalanceTypeEnum.BALANCE), () -> new GlobalRunTimeException(ErrorCode.PAYMENT_FAILED));
            } else {//如果支付账户有钱 但是不够
                //先扣除支付账户
                Assert.isTrue(userWalletService.subBalance(userWallet, payBalance, tradingPoolOrder.getId(), WalletDetailTypeEnum.TRANSACTION_POOL_PAYMENT, UserBalanceTypeEnum.PAY_BALANCE), () -> new GlobalRunTimeException(ErrorCode.PAYMENT_FAILED));
                payOrder.setPayBalanceAmount(payBalance);
                BigDecimal subtract = payAmount.subtract(payBalance);
                Assert.isTrue(userWalletService.subBalance(userWallet, subtract, tradingPoolOrder.getId(), WalletDetailTypeEnum.TRANSACTION_POOL_PAYMENT, UserBalanceTypeEnum.BALANCE), () -> new GlobalRunTimeException(ErrorCode.PAYMENT_FAILED));
                payOrder.setBalanceAmount(subtract);
                tradingPoolOrder.setPayBalancePrice(payBalance);
            }
        }
        // 入库
        payOrderDao.save(payOrder);
        DigitalCollection digitalCollection = digitalCollectionDao.getById(tradingPoolConfig.getCollectionId());

        // 给用户发NFT,nft也要先发掉,否则数据填充不进去.
        UserCollection userCollection = userCollectionService.addCollection(userInfo.getId(), null, digitalCollection.getId(), IdUtil.getSnowflakeNextIdStr(), digitalCollection.getCreationContent(), digitalCollection.getLevel(), digitalCollection.getFirstTitle(), digitalCollection.getUserCreatorId(), ReceivedCollectionType.TRADING_POOL, 0L, "", "", Boolean.TRUE, 0L, tradingPoolOrder.getId(), 0L, tradingPoolOrder.getPrice().divide(BigDecimal.ONE), 0, null, null, AddCollectionEnum.TRADING_POOL, null, null, false);

        tradingPoolOrder.setNumbering(userCollection.getNumbering());

        tradingPoolOrderDao.save(tradingPoolOrder);

        //更新当前价格.更新当前库存,更新当前资金池
        Boolean inventoryDeduction = tradingPoolConfigDao.buyInventoryDeduction(tradingPoolConfig.getId(), tradingPoolOrder.getPrice(), digitalCollection.getSumCount());
        if (!inventoryDeduction) {
            throw new GlobalRunTimeException("当前库存异常");
        }
        // 添加共识度
        userConsensusService.adjustConsensusService(userInfo.getId(), digitalCollection.getId(), BigDecimal.ONE, UserConsensusTypeEnums.TRADING_POOL_BUY, digitalCollection.getFirstTitle(), null);


        TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
            @Override
            public void afterCommit() {
                // 发送mq来同步今日成交量 最高价和最低价
                messageQueueHelper.sendMessage(MessageTopic.QUANTITY_AND_PRICE_SYNCHRONIZATION, tradingPoolConfig.getId(), TradingPoolMessageEnum.QUANTITY_AND_PRICE_SYNCHRONIZATION.name(), TradingPoolMessageEnum.QUANTITY_AND_PRICE_SYNCHRONIZATION.getDesc() + tradingPoolOrder.getId());
            }
        });
    }


    private IPage<TradingPoolOrderVO> getTradingPoolOrderVOIPage(Page<TradingPoolOrder> tradingPoolOrderPage) {
        Map<Long, DigitalCollection> map = getDigitalCollectionMap(tradingPoolOrderPage.getRecords());
        return tradingPoolOrderPage.convert(x -> {
            TradingPoolOrderVO tradingPoolOrderVO = new TradingPoolOrderVO();
            DigitalCollection digitalCollection = map.get(x.getCollectionId());
            if (Objects.nonNull(digitalCollection)) {
                tradingPoolOrderVO.setFirstTitle(digitalCollection.getFirstTitle());
                tradingPoolOrderVO.setCreationContent(digitalCollection.getCreationContent());
            }
            tradingPoolOrderVO.setId(x.getId());
            tradingPoolOrderVO.setNumbering(String.valueOf(x.getNumbering()));
            tradingPoolOrderVO.setAmount(x.getPrice());
            tradingPoolOrderVO.setDealTime(x.getDealTime());
            tradingPoolOrderVO.setOrderNumber(x.getOrderNo());
            tradingPoolOrderVO.setOrderStatus(TradingStatusEnum.SUCCESS);
            return tradingPoolOrderVO;
        });
    }


    public Map<Long, DigitalCollection> getDigitalCollectionMap(List<TradingPoolOrder> records) {
        List<Long> collectionIds = records.stream().map(TradingPoolOrder::getCollectionId).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(collectionIds)) {
            return new HashMap<>();
        }
        List<DigitalCollection> digitalCollections = digitalCollectionDao.listByIds(collectionIds);
        return digitalCollections.stream().collect(Collectors.toMap(DigitalCollection::getId, obj -> obj));
    }
}




